// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/heap/embedder-tracing.h"

#include "src/base/logging.h"
#include "src/objects/embedder-data-slot.h"
#include "src/objects/js-objects-inl.h"

namespace v8 {
namespace internal {

    void LocalEmbedderHeapTracer::SetRemoteTracer(EmbedderHeapTracer* tracer)
    {
        if (remote_tracer_)
            remote_tracer_->isolate_ = nullptr;

        remote_tracer_ = tracer;
        if (remote_tracer_)
            remote_tracer_->isolate_ = reinterpret_cast<v8::Isolate*>(isolate_);
    }

    void LocalEmbedderHeapTracer::TracePrologue()
    {
        if (!InUse())
            return;

        num_v8_marking_worklist_was_empty_ = 0;
        embedder_worklist_empty_ = false;
        remote_tracer_->TracePrologue();
    }

    void LocalEmbedderHeapTracer::TraceEpilogue()
    {
        if (!InUse())
            return;

        remote_tracer_->TraceEpilogue();
    }

    void LocalEmbedderHeapTracer::EnterFinalPause()
    {
        if (!InUse())
            return;

        remote_tracer_->EnterFinalPause(embedder_stack_state_);
        // Resetting to state unknown as there may be follow up garbage collections
        // triggered from callbacks that have a different stack state.
        embedder_stack_state_ = EmbedderHeapTracer::kUnknown;
    }

    bool LocalEmbedderHeapTracer::Trace(double deadline)
    {
        if (!InUse())
            return true;

        return remote_tracer_->AdvanceTracing(deadline);
    }

    bool LocalEmbedderHeapTracer::IsRemoteTracingDone()
    {
        return !InUse() || remote_tracer_->IsTracingDone();
    }

    void LocalEmbedderHeapTracer::SetEmbedderStackStateForNextFinalization(
        EmbedderHeapTracer::EmbedderStackState stack_state)
    {
        if (!InUse())
            return;

        embedder_stack_state_ = stack_state;
    }

    LocalEmbedderHeapTracer::ProcessingScope::ProcessingScope(
        LocalEmbedderHeapTracer* tracer)
        : tracer_(tracer)
    {
        wrapper_cache_.reserve(kWrapperCacheSize);
    }

    LocalEmbedderHeapTracer::ProcessingScope::~ProcessingScope()
    {
        if (!wrapper_cache_.empty()) {
            tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_));
        }
    }

    void LocalEmbedderHeapTracer::ProcessingScope::TracePossibleWrapper(
        JSObject js_object)
    {
        DCHECK(js_object->IsApiWrapper());
        if (js_object->GetEmbedderFieldCount() < 2)
            return;

        void* pointer0;
        void* pointer1;
        if (EmbedderDataSlot(js_object, 0).ToAlignedPointer(&pointer0) && pointer0 && EmbedderDataSlot(js_object, 1).ToAlignedPointer(&pointer1)) {
            wrapper_cache_.push_back({ pointer0, pointer1 });
        }
        FlushWrapperCacheIfFull();
    }

    void LocalEmbedderHeapTracer::ProcessingScope::FlushWrapperCacheIfFull()
    {
        if (wrapper_cache_.size() == wrapper_cache_.capacity()) {
            tracer_->remote_tracer()->RegisterV8References(std::move(wrapper_cache_));
            wrapper_cache_.clear();
            wrapper_cache_.reserve(kWrapperCacheSize);
        }
    }

    void LocalEmbedderHeapTracer::ProcessingScope::AddWrapperInfoForTesting(
        WrapperInfo info)
    {
        wrapper_cache_.push_back(info);
        FlushWrapperCacheIfFull();
    }

} // namespace internal
} // namespace v8
